#include <QFile>
#include <QXmlStreamWriter>

#include "rssreader.h"
#include "rssfeed.h"
#include "functions.h"
#include "formmain.h"
#include "definitions.h"


RssReader::RssReader(const QString &path) {
    feeds = new QList<RssFeed*>();
    this->path = path;

    stateNewIcon = QIcon(":/graph/graphics/state-new.png");
    stateNormalIcon = QIcon(":/graph/graphics/state-normal.png");
    stateProblemIcon = QIcon(":/graph/graphics/state-problem.png");
}

void RssReader::deleteLater() {
	QObject::deleteLater();
}

QList<RssFeed*> *RssReader::getFeeds() {
    return feeds;
}

RssFeed *RssReader::getFeedByName(const QString &name) {
    foreach (RssFeed *feed, *getFeeds())
        if (name == feed->getTitle())
            return feed;
    return NULL;
}

QList<RssFeed*> RssReader::getFeedsByCategory(const QString &category) {
    QList<RssFeed*> list;
    foreach (RssFeed *feed, *getFeeds())
        if (category == feed->getCategory())
            list.append(feed);
    return list;
}

void RssReader::removeFeed(const QString &name) {
	getFeeds()->removeOne(getFeedByName(name));
}

QStringList RssReader::getCategories() {
    QStringList categories;
    foreach (RssFeed *feed, *getFeeds())
        if (!categories.contains(feed->getCategory()))
            categories.append(feed->getCategory());
    return categories;
}

RssItem *RssReader::getItemByLink(const QString &link) {
    foreach (RssFeed *feed, *getFeeds()) {
        if (feed->getItemByLink(link))
            return feed->getItemByLink(link);
    }
    return NULL;
}

void RssReader::trashAllMessages() {
    foreach (RssFeed *feed, *getFeeds())
        feed->trashAllMessages();
}

void RssReader::unhideMessages() {
    foreach (RssFeed *feed, *getFeeds())
        feed->unhideMessages();
}

void RssReader::hideMessages(bool onlySeen) {
    foreach (RssFeed *feed, *getFeeds())
        feed->hideMessages(onlySeen);
}

int RssReader::countOfUnseenMessages() {
    int count = 0;
    foreach (RssFeed *feed, *getFeeds())
        count += feed->countOfUnseenMessages();
    return count;
}

void RssReader::markReader(bool read) {
    foreach (RssFeed *feed, *getFeeds())
        feed->markAllVisibleMessages(read);
}

void RssReader::editCategory(const QString &oldCategory, const QString &newCategory) {
    foreach (RssFeed *feed, getFeedsByCategory(oldCategory))
        feed->setCategory(newCategory);
}

bool RssReader::addFeed(RssFeed *feed) {
    if (feed == NULL || !canBeAdded(feed->getTitle(), feed->getLink())) return false;
    getFeeds()->append(feed);
    return true;
}

bool RssReader::canBeAdded(const QString &title, const QString &link) {
    foreach (RssFeed *feeed, *getFeeds())
        if (feeed->getLink() == link || feeed->getTitle() == title)
            return false;
    return true;
}

bool RssReader::canBeEdited(RssFeed *oldOne, RssFeed *newOne) {
    foreach (RssFeed *feeed, *getFeeds())
        if (feeed != oldOne && (feeed->getLink() == newOne->getLink() || feeed->getTitle() == newOne->getTitle()))
            return false;
    return true;
}

bool RssReader::editFeed(RssFeed *oldOne, RssFeed *newOne) {
    if (newOne == NULL || !canBeEdited(oldOne, newOne)) return false;
    if (oldOne->getLink() != newOne->getLink() || oldOne->getEncoding() != newOne->getEncoding())
        oldOne->trashAllMessages();
    oldOne->setTitle(newOne->getTitle())->setLink(newOne->getLink())->setCategory(newOne->getCategory())->setType(newOne->getType())->setEncoding(newOne->getEncoding());
    return true;
}

void RssReader::updateRepresentation(QTreeWidget *widget) {
    widget->clear();
	foreach (QString category, getCategories()) {
		if (category != "")
			widget->addTopLevelItem(new QTreeWidgetItem(QStringList() << category));
	}

    foreach (RssFeed *feed, *getFeeds()) {
        QTreeWidgetItem *item = new QTreeWidgetItem();
        item->setText(0, feed->getTitle());
		if (feed->getCategory() == "")
			widget->addTopLevelItem(item);
		else
			widget->findItems(feed->getCategory(), Qt::MatchFixedString).at(0)->addChild(item);
    }
    updateColors(widget);
    FormMain::expandTreeWidget(widget);
}

void RssReader::updateColors(QTreeWidget *widget) {
    QTreeWidgetItemIterator it(widget);

    QFont regularFont;
    QFont boldFont;
    boldFont.setBold(true);

    RssFeed *feed;
    QNetworkReply::NetworkError error;
    bool hasUnseen;
    int countOfAll;
    int countOfUnread;

    while (*it) {
		if (!(*it)->childCount()) {
            feed = getFeedByName((*it)->text(0));
#ifdef DEBUG
            Q_ASSERT(feed);
#endif
            hasUnseen = feed->hasUnseenMessages();
            error = feed->getState();
            countOfUnread = feed->countOfUnseenMessages();
			countOfAll = feed->countOfVisibleMessages();

            if (error != QNetworkReply::NoError) {
                (*it)->setIcon(0, stateProblemIcon);
                (*it)->setFont(0, hasUnseen ? boldFont : regularFont);
            }
            else if (hasUnseen) {
                (*it)->setFont(0, boldFont);
                (*it)->setIcon(0, stateNewIcon);
            }
            else {
                (*it)->setFont(0, regularFont);
                (*it)->setIcon(0, stateNormalIcon);
            }

            (*it)->setText(1, QString::number(countOfUnread));
            (*it)->setText(2, QString::number(countOfAll));

			(*it)->setFont(1, boldFont);

            QString message = tr("Title: %1").arg(feed->getTitle()) + '\n' +
                              tr("Link: %1").arg(feed->getLink()) + '\n' +
                              tr("Category: %1").arg(feed->getCategory()) + '\n' +
                              tr("Type: %1").arg(Functions::feedTypeToQstring(feed->getType())) + '\n' +
                              tr("Unread messages: %1").arg(QString::number(countOfUnread));
            if (error)
                message.append("\n\n" + FormMain::getNetCodeError(error));
            (*it)->setToolTip(0, message);
        }
        else {
            // category
            (*it)->setFont(0, boldFont);
        }

        it++;
    }
    // resize "count" columns
    widget->resizeColumnToContents(1);
    widget->resizeColumnToContents(2);
}

void RssReader::removeCategory(const QString &category) {
    foreach (RssFeed *feed, getFeedsByCategory(category))
        getFeeds()->removeOne(feed);
}

void RssReader::loadOpml(bool append, const QString &filename) {
    if (!append) feeds->clear();
    QFile file(filename);
    if (!file.open(QFile::ReadOnly)) {
        QMessageBox::information(0, tr("OPML File Error"), tr("Cannot open OPML file %1 for reading").arg(filename));
        return;
    }
    QDomDocument doc;
    doc.setContent(file.readAll());
    if (doc.documentElement().attribute("version") == "1.0") {
        QDomNodeList outerOutlines = doc.documentElement().namedItem("body").toElement().childNodes();
        for (int i = 0; i < outerOutlines.size(); i++) {
            if (outerOutlines.at(i).toElement().attribute("text").isNull()) {
                qDebug() << tr("%1RssReader::loadOpml: Element text is null. Skipping.").arg(APP_ID);
                continue;
            }
            QDomNodeList innerOutline = outerOutlines.at(i).toElement().childNodes();
            for (int j = 0; j < innerOutline.size(); j++) {
                QDomElement elem = innerOutline.at(j).toElement();
                // url attribute is standard. But xmlUrl is used
                // in various OPML sample files found in the public.
                QString url(elem.attribute("url"));
                if (url.isNull())
                    url = elem.attribute("xmlUrl");
                if ((!elem.attribute("text").isNull()) && (!url.isNull()))
                    addFeed(new RssFeed(elem.attribute("text"), url, outerOutlines.at(i).toElement().attribute("text"), FEED_RSS));
                else {
                    qDebug() << tr("%1RssReader::loadOpml: text and url does not exist").arg(APP_ID);
                    continue;
                }
            }
        }
    }
    else {
        qDebug() << tr("%1RssReader::loadOpml: XML version is not 1.0").arg(APP_ID);
    }
}

void RssReader::saveOpml(const QString &filename) {
    QFile file(filename);
    if (!file.open(QFile::WriteOnly | QFile::Text)) return;
    QXmlStreamWriter writer(&file);
    writer.setAutoFormatting(true);
    writer.setAutoFormattingIndent(4);
    writer.writeStartDocument();
    writer.writeStartElement("opml");
    writer.writeAttribute("version", "1.0");
    writer.writeStartElement("head");
    writer.writeTextElement("title", tr("Exported feeds (RSS Guard)"));
    writer.writeEndElement();
    writer.writeStartElement("body");
    foreach (QString category, getCategories()) {
        writer.writeStartElement("outline");
        writer.writeAttribute("text", category);
        foreach (RssFeed *feed, getFeedsByCategory(category)) {
            writer.writeStartElement("outline");
            writer.writeAttribute("text", feed->getTitle());
            writer.writeAttribute("url", feed->getLink());
            writer.writeEndElement();
        }
        writer.writeEndElement();
    }
    writer.writeEndElement();
    writer.writeEndElement();
    writer.writeEndDocument();
    file.close();
}

void RssReader::loadXml(bool append, const QString &filename) {
    if (!append) feeds->clear();
    QFile file(filename);
    if (!file.open(QFile::ReadOnly)) return;
    QString lastCategory("");
    QString lastTitle("");
    QXmlStreamReader reader;
    reader.setDevice(&file);
    reader.readNext();
    while (!reader.atEnd()) {
        if (reader.isStartElement()) {
            if (reader.name() == "category")
                lastCategory = reader.attributes().value("name").toString();
            if (reader.name() == "feed") {
                addFeed(new RssFeed(reader.attributes().value("title").toString(), reader.attributes().value("link").toString(), lastCategory, Functions::qstrToFeedType(reader.attributes().value("type").toString()), reader.attributes().value("encoding").toString(), (QNetworkReply::NetworkError) reader.attributes().value("state").toString().toInt()));
                lastTitle = reader.attributes().value("title").toString();
            }
            if (reader.name() == "message")
                getFeedByName(lastTitle)->addMessage(reader.attributes().value("title").toString(), reader.attributes().value("link").toString(), reader.attributes().value("description").toString(), reader.attributes().value("date").toString(), Functions::qstrToBool(reader.attributes().value("seen").toString()), Functions::qstrToBool(reader.attributes().value("hidden").toString()));
        }
        reader.readNext();
    }
    file.close();

    qDebug() << tr("%1Feeds were loaded. Used file: %2").arg(APP_ID, filename);
}

bool RssReader::saveXml(const QString &filename) {
    QFile file(filename);
    if (!file.open(QFile::WriteOnly | QFile::Text)) return false;
    QXmlStreamWriter writer(&file);
    writer.setAutoFormatting(true);
    writer.setAutoFormattingIndent(4);
    writer.writeStartDocument();
    writer.writeStartElement("feeds");
    foreach (QString category, getCategories()) {
        writer.writeStartElement("category");
        writer.writeAttribute("name", category);
        foreach (RssFeed *feed, getFeedsByCategory(category)) {
            writer.writeStartElement("feed");
            writer.writeAttribute("title", feed->getTitle());
            writer.writeAttribute("link", feed->getLink());
            writer.writeAttribute("type", Functions::feedTypeToQstring(feed->getType()));
            writer.writeAttribute("encoding", feed->getEncoding());
            writer.writeAttribute("state", QString::number(feed->getState()));
            foreach (RssItem *message, *feed->getMsgs()) {
                writer.writeStartElement("message");
                writer.writeAttribute("title", message->getTitle());
                writer.writeAttribute("link", message->getLink());
                writer.writeAttribute("date", message->getDate());
                writer.writeAttribute("seen", message->isSeen() ? "true" : "false");
                writer.writeAttribute("hidden", message->isHidden() ? "true" : "false");
                writer.writeAttribute("description", message->getDescription());
                writer.writeEndElement();
            }
            writer.writeEndElement();
        }
        writer.writeEndElement();
    }
    writer.writeEndElement();
    writer.writeEndDocument();
    file.close();
    qDebug() << tr("%1Feeds were saved. Used file: %2").arg(APP_ID, filename);
    return true;
}
